package com.faceye.core.service.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;

import com.faceye.core.entity.BaseEntity;
import com.faceye.core.manager.BaseManager;
import com.faceye.core.service.BaseService;
import com.faceye.core.util.bean.BeanMapper;
import com.faceye.core.util.result.PageResult;
import com.google.common.collect.Lists;
import org.springframework.transaction.annotation.Transactional;

/**
 * 
 * @author haipenge
 *
 * @param <DTO>
 * @param <ID>
 * @param <T>
 * @param <M>
 */
public abstract class BaseServiceImpl<DTO, ID extends Serializable, T extends BaseEntity, M extends BaseManager<T, ID>> implements BaseService<DTO, ID, M> {
	protected Logger logger = LoggerFactory.getLogger(getClass());
	protected M manager = null;
	protected Class<DTO> dtoClass = null;
	protected Class<T> entityClass = null;

	/**
	 * 初始化
	 * 
	 * @param manager
	 */
	public BaseServiceImpl(M manager) {
		ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
		Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
		for (Type actualTypeArgument : actualTypeArguments) {
			logger.debug(">>FaceYe actualTypeArgument type is:" + actualTypeArgument.getTypeName());
		}
		if (actualTypeArguments.length >= 3) {
			this.setDTOClass((Class<DTO>) actualTypeArguments[0]);
			this.setEntityClass((Class<T>) actualTypeArguments[2]);
		}
		this.manager = manager;
	}

	@Transactional
	@Override
	public DTO save(DTO dto) {
		T entity = BeanMapper.map(dto, getEntityClass());
		wrapEntity(dto,entity);
		entity = manager.save(entity);
		dto = BeanMapper.map(entity, getDTOClass());
		wrapDTO(entity, dto);
		return dto;
	}

	public DTO get(ID id) {
		T entity = this.manager.get(id);
		DTO dto = BeanMapper.map(entity, getDTOClass());
		wrapDTO(entity, dto);
		return dto;
	}
	@Transactional
	public Boolean remove(ID... id) {
		Boolean res = false;
		try {
			for (ID cid : id) {
				this.manager.remove(cid);
			}
			res = true;
		} catch (Exception e) {
			logger.error(">>FaceYe throws Exception when remove by ids,err:{}", e);
		}
		return res;
	}

	public List<DTO> getAll() {
		List<T> entities = this.manager.getAll();
		return entities == null ? null : wrapDTOs(entities, Lists.newArrayList());
	}

	/**
	 * @param page
	 *            :[1,2,3..]
	 * @param size
	 *            : if 0-> all.
	 */

	public PageResult<DTO> getPage(Map<String, Object> searchParams, int page, int size) {
		PageResult<DTO> result = new PageResult<DTO>();
		Page<T> entities = this.manager.getPage(searchParams, page, size);
		List<DTO> items = wrapDTOs(entities.getContent(), Lists.newArrayList());
		result.setItems(items);
		result.setPage(page);
		result.setSize(size);
		result.setTotal(entities.getTotalElements());
		result.setTotalPage(entities.getTotalPages());
		return result;
	}

	/**
	 * 对列表数据进行包装
	 * 
	 * @param entities
	 * @param dtos
	 * @return
	 * @Desc:
	 * @Author:haipenge
	 * @Date:2018年4月19日 下午6:46:20
	 */
	protected List<DTO> wrapDTOs(List<T> entities, List<DTO> dtos) {
		if (CollectionUtils.isNotEmpty(entities)) {
			for(T entity:entities){
				DTO dto = BeanMapper.map(entity, getDTOClass());
				logger.debug(">>FaceYe entity class is:"+entity.getClass().getName()+",dto class is:"+dto.getClass().getName());
				wrapDTO(entity, dto);
				dtos.add(dto);
			}
//			entities.forEach((T entity) -> {
//				DTO dto = BeanMapper.map(entity, getDTOClass());
//				wrapDTO(entity, dto);
//				dtos.add(dto);
//			});
		}
		return dtos;
	}

	/**
	 * 根据Entity，对DTO进行包装，用于数据传递前的置换
	 * 
	 * @param entity
	 * @param dto
	 * @Desc:
	 * @Author:haipenge
	 * @Date:2018年4月19日 下午6:41:51
	 */
	 abstract protected  void wrapDTO(T entity, DTO dto);

	/**
	 * 根据DTO，对Entity进行包装，用于数据存储
	 * 
	 * @param dto
	 * @param entity
	 * @Desc:
	 * @Author:haipenge
	 * @Date:2018年4月19日 下午6:42:11
	 */
	abstract protected  void wrapEntity(DTO dto, T entity);

	/**
	 * 设置DTO泛型类
	 * 
	 * @param dtoClass
	 * @Desc:
	 * @Author:haipenge
	 * @Date:2018年4月19日 下午6:42:57
	 */
	protected void setDTOClass(Class<DTO> dtoClass) {
		this.dtoClass = dtoClass;
	}

	protected Class<DTO> getDTOClass() {
		logger.debug(">>getDTOClass is:"+dtoClass.getName());
		return dtoClass;
	}

	/**
	 * 设置Entity泛型类
	 * 
	 * @param entityClass
	 * @Desc:
	 * @Author:haipenge
	 * @Date:2018年4月19日 下午6:43:20
	 */
	protected void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	protected Class<T> getEntityClass() {
		logger.debug(">>FaceYe getEntityClass is:"+entityClass.getName());
		return entityClass;
	}

}
